package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SearchService;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    @Override
    public Map search(Map<String, String> searchMap) {
        Map<String,Object> resultMap = new HashMap<>();
        //构建查询//有条件才查询Es
        if (!(searchMap==null)){
            //创建条件查询对象//组合条件对象
            NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
            //因为是复杂多个查询。这里用bool查询.要判断传过来的条件是否为空
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            //0:关键词
            if (!StringUtils.isEmpty(searchMap.get("keywords"))){
                //查询满足分词查询的条件
                boolQueryBuilder.must(QueryBuilders.matchQuery("name",searchMap.get("keywords")).operator(Operator.AND));
            }

            //按照品牌查询
            //1:条件 品牌
            if (!StringUtils.isEmpty(searchMap.get("brand"))){
                boolQueryBuilder.filter(QueryBuilders.termQuery("brandName",searchMap.get("brand")));
            }

            //http://localhost:9009/search?keywords=手机&spec_颜色=红色&spec_网络制式=电信3G&spec_版本=8G%2B256G
            //按照传过来的规格进行查询过滤
            //2:条件 规格
            for (String key : searchMap.keySet()) {
                //先遍历前端传回来的数据
                if (key.startsWith("spec_")){
                    //拿到要查询的spec_后面的内容
                    String value = searchMap.get(key).replace(" ","+");
                    boolQueryBuilder.filter(QueryBuilders.termQuery("specMap."+key.substring(5)+".keyword",value));
                }
            }

            //价格区间查询 0-500 500-1000 3000
            //3:条件 价格
            if (!StringUtils.isEmpty(searchMap.get("price"))){
                String[] prices = searchMap.get("price").split("-");
                if (prices.length==2){
                    //<500
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(prices[1]));
                }
                //>0  或者 >3000
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(prices[0]));
            }

            //传入对应的查询条件
            nativeSearchQueryBuilder.withQuery(boolQueryBuilder);

            //5.高亮
            HighlightBuilder.Field field = new HighlightBuilder.Field("name");
            field.preTags("<span style='color:red'>");
            field.postTags("</span>");
            nativeSearchQueryBuilder.withHighlightFields(field);

            //根据name聚合手机品牌
            //6. 品牌聚合(分组)查询
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName").size(10));
            //根据用户输入name以后聚合对应的型号参数信息
            //7. 规格聚合(分组)查询
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword"));
            //8.排序
            if (!StringUtils.isEmpty(searchMap.get("sortField")) && !StringUtils.isEmpty(searchMap.get("sortRule"))){
                if ("ASC".equals(searchMap.get("sortRule"))){
                    nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.ASC));
                }else {
                    nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.DESC));
                }
            }
            //9: 分页
            String pageNum = searchMap.get("pageNum"); //当前页码
            String pageSize = searchMap.get("pageSize"); //当前也展示多少条数据
            if (StringUtils.isEmpty(pageNum)){
                pageNum="1";
            }
            if (StringUtils.isEmpty(pageSize)){
                pageSize="30";
            }
            SpringDataWebProperties.Pageable pageable = new SpringDataWebProperties.Pageable();
            nativeSearchQueryBuilder.withPageable(PageRequest.of(Integer.parseInt(pageNum)-1,Integer.parseInt(pageSize)));


            //10: 执行查询, 返回结果对象
            AggregatedPage<SkuInfo> skuInfo = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                    //把查询到的结果封装到List<skuInfo>集合中
                    List<T> skuList = new ArrayList<>();
                    SearchHits hits = searchResponse.getHits();
                    for (SearchHit hit : hits) {
                        SkuInfo skuInfo1 = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
                        //高亮替换
                        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                        HighlightField fields = highlightFields.get("name");
                        if (fields!=null){
                            Text[] fragments = fields.getFragments();
                            StringBuilder sb = new StringBuilder();
                            for (Text fragment : fragments) {
                                sb.append(fragment);
                            }
                            String string = sb.toString();
                            skuInfo1.setName(string);
                        }
                        skuList.add((T) skuInfo1);
                    }
                    //返回数据 分页对象 击中的数据数 聚合
                    return new AggregatedPageImpl<>(skuList,pageable,hits.getTotalHits(),searchResponse.getAggregations());
                }
            });
            //根据前端沟通。返回的数据具体是什么
            //总记录数
            // 11. 总条数
            resultMap.put("total",skuInfo.getTotalElements());
            //总页数
            //12. 总页数
            resultMap.put("totalPages",skuInfo.getTotalPages());
            //查询到的数据集合
            //13. 查询结果集合
            resultMap.put("rows",skuInfo.getContent());
            //聚合后的数据封装
            //14. 获取品牌聚合结果
            List<String> brandList = new ArrayList<>();
            Aggregations aggregations = skuInfo.getAggregations();
            Map<String, Aggregation> map = aggregations.getAsMap();
            Terms brandTerms = (Terms) map.get("skuBrand");
            List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
            for (Terms.Bucket bucket : buckets) {
                brandList.add(bucket.getKeyAsString());
            }
            resultMap.put("brandList",brandList);
            //封装规格分组结果
            //15. 获取规格聚合结果
            List<String> specList = new ArrayList<>();
            Terms specTerms = (Terms) map.get("skuSpec");
            List<? extends Terms.Bucket> specTermsBuckets = specTerms.getBuckets();
            for (Terms.Bucket specTermsBucket : specTermsBuckets) {
                specList.add(specTermsBucket.getKeyAsString());
            }
            resultMap.put("specList",this.formartSpec(specList));
            //16. 返回当前页
            resultMap.put("pageNum",pageNum);
            return resultMap;
        }
        return null;
    }

    /**
     *
     * specList:
     * "{'颜色': '蓝色', '版本': '6GB+128GB'}",
     *         "{'颜色': '黑色', '版本': '6GB+128GB'}",
     *         "{'颜色': '黑色', '版本': '4GB+64GB'}",
     *         "{'颜色': '蓝色', '版本': '4GB+64GB'}",
     *         "{'颜色': '蓝色', '版本': '6GB+64GB'}",
     *         "{'颜色': '黑色', '版本': '6GB+64GB'}",
     *         "{'颜色': '黑色'}",
     *         "{'颜色': '蓝色'}",
     *         "{'颜色': '金色', '版本': '4GB+64GB'}",
     *         "{'颜色': '粉色', '版本': '6GB+128GB'}"
     *
     *需求数据    {
     *     颜色:[粉色，蓝色]
     *     版本:[4GB+64GB,6GB+128GB]
     *}
     * @param specList
     * @return
     */
    public Map<String,Set<String>> formartSpec(List<String> specList){
        Map<String,Set<String>> result = new HashMap<>();
        for (String spec : specList) {
            //spec: "{"颜色":"蓝色"，"版本":"6GB+128GB"}"
            //{
            // "颜色":"蓝色",
            //"版本":"6GB+128GB"
            // }
            Map<String,String> map = JSON.parseObject(spec, Map.class);
            for (String key : map.keySet()) {
                //key:颜色 value:蓝色
                //specValue:[蓝色，黑色，粉色]
                Set<String> specValue = result.get(key);
                if (specValue==null){
                    specValue=new HashSet<>();
                }
                //
                specValue.add(map.get(key));

                result.put(key,specValue);
            }
        }
        return result;
    }
}
